package com.example.zyl.common.utils;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.handler.EntityListHandler;
import cn.hutool.db.sql.SqlExecutor;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.example.zyl.common.query.annotation.SearchField;
import com.example.zyl.common.query.query.BaseQueryDto;
import com.example.zyl.common.utils.constant.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

@Slf4j
public class ToolUtils {

    /**
     * 判断对象是否非空
     *
     * @param obj 需要判断的对象
     * @return 如果对象非空返回 true，否则返回 false
     */
    public static boolean isNotEmpty(Object obj) {
        return !isEmpty(obj);
    }


    /**
     * 判断对象是否为空
     *
     * @param obj 需要判断的对象
     * @return 如果对象为空返回 true，否则返回 false
     */
    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true; // 判断 null
        }

        // 判断字符串，去掉前后空格
        if (obj instanceof String) {
            return ((String) obj).trim().isEmpty();
        }

        // 判断集合
        if (obj instanceof Collection) {
            return ((Collection<?>) obj).isEmpty();
        }

        // 判断数组
        if (obj.getClass().isArray()) {
            return Array.getLength(obj) == 0;
        }

        // 判断映射
        if (obj instanceof Map) {
            return ((Map<?, ?>) obj).isEmpty();
        }
        // 对于任何其他类型，默认返回 false
        return false;
    }

    /**
     * 判断字符串是否包含指定字符串，忽略大小写
     *
     * @param str  字符串
     * @param str2 需要查找的字符串
     * @return 如果包含返回 true，否则返回 false
     */
    public static boolean containsIgnoreCase(String str, String str2) {
        return str.toLowerCase().contains(str2.toLowerCase());
    }


    /**
     * 参数拼装
     */
    public static String argsArrayToString(Object[] paramsArray) {
        StringJoiner params = new StringJoiner(" ");
        if (ArrayUtil.isEmpty(paramsArray)) {
            return params.toString();
        }
        for (Object o : paramsArray) {
            if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                params.add(JSONUtil.toJsonStr(o));
            }
        }
        return params.toString();
    }

    /**
     * 拼接路径Key
     */
    public static String concatKey(Object[] arg, Boolean isIp) {
        HttpServletRequest request = ServletUtils.getRequest();
        String url = request.getRequestURI();
        String ip = "";
        if (isIp) {
            ip = ClientIpUtil.getClientRealIp(request);
        }
        String param = ToolUtils.argsArrayToString(arg);
        return EncryptUtils.encryptByMd5(url + ip + param, null);
    }


    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public static boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.values()) {
                return value instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }


    /**
     * 判断对象是否为对象类型
     *
     * @param obj 对象
     * @return 如果对象为对象类型返回 true，否则返回 false
     */
    public static boolean isObjectType(Object obj) {
        if (obj == null) {
            return true;
        }
        // 检查是否为基本数据类型
        if (obj.getClass().isPrimitive()) {
            return true;
        }
        // 检查是否为包装类型、如果不是上述类型，则是对象类型
        return obj instanceof Integer || obj instanceof Long || obj instanceof Float ||
                obj instanceof Double || obj instanceof Character || obj instanceof Boolean ||
                obj instanceof Byte || obj instanceof Short;
    }


    /**
     * 根据类名获取sql语句
     *
     * @param clazz 类名
     * @return sql语句
     */
    public static String getByClassSql(Class<?> clazz) {
        // 校验类名不能为空
        ConditionUtils.throwException(clazz).accept("类不能为空！");
        // 获取类中的字段
        Field[] declaredFields = clazz.getDeclaredFields();
        // 组装sql语句
        StringBuilder sql = new StringBuilder();

        sql.append(Constant.SELECT);
        // 循环处理类中的字段
        for (Field field : declaredFields) {
            field.setAccessible(true);
            // 获取每个字段的字段名
            String fieldName = field.getName();
            // 转换为下划线命名
            String underlineCase = StrUtil.toUnderlineCase(fieldName);
            // 组装sql语句
            sql.append(underlineCase);
            sql.append(Constant.AS);
            sql.append(fieldName);
            sql.append(Constant.SEPARATOR);
        }
        // 去掉最后一个逗号
        sql.deleteCharAt(sql.length() - 1);
        // 获取类名
        String className = clazz.getSimpleName();
        // 组装sql语句
        sql.append(Constant.FROM);
        sql.append(StrUtil.toUnderlineCase(className));
        return sql.toString();
    }

    /**
     * 插入数据sql语句
     *
     * @param clazz 类名
     * @return sql语句
     */
    public static String insertByClassSql(Class<?> clazz, int size) {
        // 校验类名不能为空
        ConditionUtils.throwException(clazz).accept("类不能为空！");
        // 获取类中的字段
        Field[] declaredFields = clazz.getDeclaredFields();
        // 组装sql语句
        StringBuilder sql = new StringBuilder();

        sql.append(Constant.INSERT_INTO);
        // 组装表名
        sql.append(StrUtil.toUnderlineCase(clazz.getSimpleName()));
        sql.append(" ( ");
        // 循环处理类中的字段
        for (Field field : declaredFields) {
            field.setAccessible(true);
            // 获取每个字段的字段名
            String fieldName = field.getName();
            // 转换为下划线命名
            String underlineCase = StrUtil.toUnderlineCase(fieldName);
            // 组装sql语句
            sql.append(underlineCase);
            sql.append(Constant.SEPARATOR);
        }
        // 去掉最后一个逗号
        sql.deleteCharAt(sql.length() - 1);
        sql.append(" ) ");
        return sql.append(insertByClassValues(clazz, size)).toString();
    }


    /**
     * 插入数据values 值
     *
     * @param clazz 类名
     * @return values 值
     */
    public static String insertByClassValues(Class<?> clazz, int size) {
        // 校验类名不能为空
        ConditionUtils.throwException(clazz).accept("对象不能为空！");
        // 获取类中的字段
        Field[] declaredFields = clazz.getDeclaredFields();
        // 组装sql语句
        StringBuilder sql = new StringBuilder();

        sql.append(Constant.VALUES);
        if (size >= 1) {
            for (int i = 0; i < size; i++) {
                sql.append(" ( ");
                // 循环处理类中的字段
                for (Field field : declaredFields) {
                    field.setAccessible(true);
                    // 获取每个字段的字段名
                    String fieldName = field.getName();
                    // 组装sql语句
                    sql.append(" ?");
                    sql.append(fieldName).append(i);
                    sql.append(Constant.SEPARATOR);
                }
                // 去掉最后一个逗号
                sql.deleteCharAt(sql.length() - 1);
                sql.append(" ) ,");
            }
        }
        sql.deleteCharAt(sql.length() - 1);
        return sql.toString();
    }


    public static void main(String[] args) {
//        SysDictVo sysDictVo = new SysDictVo();
//        sysDictVo.setSex("1");
//        sysDictVo.convert();
//        System.out.println(sysDictVo.toString());
    }


    /**
     * 拼接where条件
     */
    public static String concatWhere(Class<?> clazz) {
        // 校验类名不能为空
        ConditionUtils.throwException(clazz).accept("类不能为空！");
        // 获取类中的字段
        Field[] fields = clazz.getDeclaredFields();
        StringJoiner where = new StringJoiner(Constant.AND);
        if (ArrayUtil.isEmpty(fields)) {
            return "";
        }
        // 循环处理类中的字段
        for (Field field : fields) {
            // 获取字段值
            Object value = ReflectUtil.getFieldValue(field, field.getName());
            // 组装where条件
            if (value != null) {
                where.add(field.getName() + Constant.EQ + Constant.PLACEHOLDER);
            }
        }

        if (isEmpty(where.toString())) {
            return "";
        }
        return Constant.WHERE.concat(where.toString());
    }

    /**
     * 获取值
     */
    public static Object[] getValue(Class<?> clazz) {
// 校验类名不能为空
        ConditionUtils.throwException(clazz).accept("类不能为空！");
        // 获取类中的字段
        Field[] fields = clazz.getDeclaredFields();
        Object[] values = new Object[fields.length];
        // 循环处理类中的字段
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            // 获取字段值
            Object value = ReflectUtil.getFieldValue(fields[i], fields[i].getName());
            // 组装where条件
            if (value != null) {
                values[i] = value;
            }
        }
        return values;
    }


    /**
     * 获取类的所有字段sql语句
     *
     * @param obj 对象
     * @return sql语句
     */
    public static String getSqlByObj(Object obj) {
        if (obj == null) {
            return "";
        }
        // 获取类名
        if (obj instanceof Collection) {
            return "";
        }
        if (isObjectType(obj)) {
            return "";
        }
        if (obj instanceof Map) {
            Map<?, ?> paramMap = ((Map<?, ?>) obj);
            if (paramMap.isEmpty()) {
                return "";
            }
            return Constant.WHERE.concat(Constant.QUERY_ALL).concat(mapToSql(paramMap));
        }
        // 获取类中的字段
        List<Field> allFields = BaseQueryDto.getQueryFields(obj.getClass());
        StringBuilder sql = new StringBuilder();
        for (Field field : allFields) {
            field.setAccessible(true);
            if (field.getAnnotation(SearchField.class) != null) {
                SearchField fieldAnnotation = field.getAnnotation(SearchField.class);
                String tableFieldName = fieldAnnotation.tableField();
                if (StrUtil.isBlank(tableFieldName)) {
                    tableFieldName = field.getName();
                }
                Object value = ReflectUtil.getFieldValue(obj, field);
                BaseQueryDto.getSql(tableFieldName, fieldAnnotation, value, sql);
            }
        }
        if (sql.length() > 0) {
            return Constant.WHERE.concat(Constant.QUERY_ALL).concat(sql.toString());
        }
        return "";
    }


    /**
     * 将对象转换为map
     *
     * @param obj 对象
     * @return map
     */
    public static Map<String, Object> objToMap(Object obj) {
        if (obj instanceof Map) {
            return (Map<String, Object>) obj;
        }
        Map<String, Object> map = new HashMap<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                SearchField annotation = field.getAnnotation(SearchField.class);
                Object o = field.get(obj);
                if (annotation != null || isNotEmpty(o)) {
                    map.put(field.getName(), field.get(obj));
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }


    /**
     * 将新增对象转换为map
     *
     * @param obj 对象
     * @return map
     */
    public static Map<String, Object> insertObjToMap(Object obj) {
        if (obj instanceof Map) {
            return (Map<String, Object>) obj;
        }
        Map<String, Object> map = new HashMap<String, Object>();
        if (obj instanceof Collection) {
            Collection<?> objects = ((Collection<?>) obj);
            int i = 0;
            for (Object object : objects) {
                Field[] fields = object.getClass().getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    try {
                        map.put(field.getName() + i, field.get(object));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                i++;
            }
            return map;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                map.put(field.getName(), field.get(obj));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }


    /**
     * 将map转换sql
     *
     * @param map map
     * @return sql
     */
    public static String mapToSql(Map<?, ?> map) {

        StringBuilder sql = new StringBuilder();

        for (Map.Entry<?, ?> entry : map.entrySet()) {
            String key = (String) entry.getKey();
            String underlineCase = StrUtil.toUnderlineCase(key);
            Object value = entry.getValue();
            if (value != null) {
                sql.append(Constant.AND);
                sql.append(underlineCase).append(Constant.EQ).append(Constant.OR).append(key);
            }
        }
        return sql.toString();
    }


    /**
     * 获取主键列名
     *
     * @return 主键列名
     */
    public static String getKeyColumn(Class<?> clazz) {

        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field declaredField : declaredFields) {
            if (declaredField.isAnnotationPresent(TableId.class)) {
                TableId tableId = declaredField.getAnnotation(TableId.class);
                if (StrUtil.isNotBlank(tableId.value())) {
                    return tableId.value();
                } else {
                    return declaredField.getName();
                }
            }
        }
        throw new RuntimeException("未找到主键列名,请检查实体类是否添加了@TableId注解");
    }


    /**
     * 根据sql查询数据
     *
     * @param sql  sql语句
     * @param args 参数
     * @return 查询结果
     */
    public static List<Entity> getRowsBySql(Class<?> clazz, String sql, Object args) {
        // 获取SqlSession
        SqlSession sqlSession = SqlHelper.sqlSession(clazz);
        // 获取连接
        try (Connection connection = sqlSession.getConnection()) {
            String logs = "==>  Preparing: " + sql + "\n" +
                    " ==>  Parameters: " + args + "\n";
            log.info(logs);
            // 为空则执行查询
            if (args == null) {
                return SqlExecutor.query(connection, sql, new EntityListHandler());
            }
            // 类型为map
            if (args instanceof Map) {
                Map<?, ?> params = (Map<?, ?>) args;
                return SqlExecutor.query(connection, sql, new EntityListHandler(), params);
            }
            // 其他类型参数
            return SqlExecutor.query(connection, sql, new EntityListHandler(), args);
        } catch (SQLException e) {
            log.error("查询数据失败 ===> {}", e.getMessage());
            throw new RuntimeException(e);
        }
    }


    /**
     * 执行插入数据
     *
     * @param sql   sql语句
     * @param args  参数
     * @param clazz 类名
     * @return 影响行数
     */
    public static int insertBySql(Class<?> clazz, String sql, Object args) {
        // 获取SqlSession
        SqlSession sqlSession = SqlHelper.sqlSession(clazz);
        // 获取连接
        try (Connection connection = sqlSession.getConnection()) {
            String logs = "==>  Preparing: " + sql + "\n" +
                    "   ==>  Parameters: " + args + "\n";
            log.info(logs);
            // 为空则执行查询
            if (args == null) {
                return SqlExecutor.execute(connection, sql, args);
            }
            // 类型为map
            if (args instanceof Map) {
                Map<String, Object> params = (Map<String, Object>) args;
                return SqlExecutor.execute(connection, sql, params);
            }
            // 其他类型参数
            return SqlExecutor.execute(connection, sql);
        } catch (SQLException e) {
            log.error("插入数据失败 ===> {}", e.getMessage());
            throw new RuntimeException(e);
        }
    }


    /**
     * 将list转换为字符串
     *
     * @param ids         数组
     * @param idSeparator 分隔符
     * @return 字符串
     */
    public static String listToString(List<Serializable> ids, String idSeparator) {
        if (ids == null || ids.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (Serializable id : ids) {
            sb.append(id).append(idSeparator);
        }
        return sb.substring(0, sb.length() - 1);
    }
}
